﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DwarfScanner
{
    class DwarfScanner
    {
        private List<Token> tokens = new List<Token>();

        private void Reset()
        {
            tokens.Clear();
        }

        private string GetTokensAsText()
        {
            string result = "";
            foreach (Token t in tokens)
            {
                result += t + "\n";
            }
            return result;
        }

        public string Scan(string[] text)
        {
            for (int i = 0; i < text.Length; ++i )
            {
                string line = StringUtils.PrepareString(text[i]);
                string[] lexemes = GetLexemes(line);

                foreach (string lexeme in lexemes)
                {
                    AddToken(lexeme, i);
                }
            }

            return GetTokensAsText();
        }

        private string[] GetLexemes(string text)
        {
            char[] separators = new char[] { ' ' };
            return text.Split(separators, StringSplitOptions.RemoveEmptyEntries);
        }

        private void AddToken(string lexeme, int lineNumber)
        {
            Token t;
            try
            {
                t = GetToken(lexeme);
            }
            catch (DwarfScannerException e)
            {
                string errMsg = "at line " + (lineNumber + 1) + ": " + e.Message;
                t = new Token(TokenType.ERROR, errMsg);
            }

            tokens.Add(t);
        }

        private Token GetToken(string lexeme)
        {
            switch (lexeme)
            {
                case "=": return new Token(TokenType.ASSIGMENT);
                case ";": return new Token(TokenType.SEMICOLON);
                case ".": return new Token(TokenType.DOT);
                case ",": return new Token(TokenType.COMMA);
                case "(": return new Token(TokenType.LEFT_PAREN);
                case ")": return new Token(TokenType.RIGHT_PAREN);
                case "[": return new Token(TokenType.LEFT_BRACKET);
                case "]": return new Token(TokenType.RIGHT_BRACKET);
                case "{": return new Token(TokenType.LEFT_BRACE);
                case "}": return new Token(TokenType.RIGHT_BRACE);
                case "+": return new Token(TokenType.PLUS);
                case "-": return new Token(TokenType.MINUS);
                case "*": return new Token(TokenType.MULTIPLICATION);
                case "/": return new Token(TokenType.DEVISION);
                case "!": return new Token(TokenType.NOT);                
                case "<": return new Token(TokenType.LT);
                case ">": return new Token(TokenType.GT);
                case "&&": return new Token(TokenType.AND);

                case "this":    return new Token(TokenType.THIS);
                case "new":     return new Token(TokenType.NEW);
                case "return":  return new Token(TokenType.RETURN);
                case "static":  return new Token(TokenType.STATIC);
                case "void":    return new Token(TokenType.VOID);
                case "main":    return new Token(TokenType.MAIN);
                case "class":   return new Token(TokenType.CLASS);
                case "public":  return new Token(TokenType.PUBLIC);
                case "if":      return new Token(TokenType.IF);
                case "else":    return new Token(TokenType.ELSE);
                case "while":   return new Token(TokenType.WHILE);

                case "int":     return new Token(TokenType.INT);
                case "boolean": return new Token(TokenType.BOOLEAN);
                case "true":    return new Token(TokenType.TRUE);
                case "false":   return new Token(TokenType.FALSE);

                default:  return DefaultAction(lexeme);
            }
        }

        private Token DefaultAction(string lexeme)
        {
            if (StringUtils.IsInditifier(lexeme))
            {
                return new Token(TokenType.ID, lexeme);
            }
            else if (StringUtils.IsInteger(lexeme))
            {
                return new Token(TokenType.INTEGER, lexeme);
            }

            throw new DwarfScannerException("Unknown token \"" + lexeme + "\"");
        }
    }
}
